package com.lqk.nio.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.Set;

public class GroupChatServer {
    // 定义属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT=6667;

    // 构造器
    // 初始化工作
    public GroupChatServer(){
        try{
            // 得到选择器
            selector=Selector.open();
            // 获取到 ServerSocketChannel
            listenChannel = ServerSocketChannel.open();
            // 绑定端口
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            // 设置非阻塞模式
            listenChannel.configureBlocking(false);
            // 将 listenChannel 注册到 selector 上
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    // 监听代码
    public void listen(){
        try{
            // 循环处理
            while (true){
                int count = selector.select();
                if (count>0){   // 表示有事件要处理
                    // 遍历得到SelectionKey 集合
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                    while (keyIterator.hasNext()){
                        SelectionKey selectionKey = keyIterator.next();
                        if (selectionKey.isAcceptable()){   // 监听到accept事件
                            SocketChannel socketChannel = listenChannel.accept();
                            // 将该 socketChannel 注册到selector上
                            socketChannel.configureBlocking(false); // 设置为非阻塞模式
                            socketChannel.register(selector,SelectionKey.OP_READ);
                            // 提示
                            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                            LocalDateTime time = LocalDateTime.now();
                            // LocalDateTime转为String
                            String localTime = df.format(time);
                            System.out.println(localTime+" : "+socketChannel.getRemoteAddress()+"上线了！");
                            socketChannel.write(Charset.forName("UTF-8").encode(localTime+" : "+"欢迎进入聊天室，请注意隐私安全！"));
                        }else if (selectionKey.isReadable()){   // 通道发生read事件，即通道是可读状态
                            // 处理读（专门写一个方法。。。）
                            readData(selectionKey);
                        }
                        // 手动将当前的selectionKey从集合中移除，防止重复操作
                        keyIterator.remove();
                    }
                }else {
                    System.out.println("等待事件触发。。。。");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 发生异常处理
        }
    }
    // 读取客户端消息
    private void readData(SelectionKey key){
        // 定义一个SocketChannel
        SocketChannel channel=null;
        try{
            // 取到关联的channel
            channel=(SocketChannel) key.channel();
            // 创建缓冲区 Buffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            // 读取通道中的消息
            int count = channel.read(byteBuffer);
            String msg="";
            // 根据count的值做处理
            if (count>0){
                // 把缓冲区的字符串转成字符串
                byteBuffer.flip();  //读写模式转换
                msg += Charset.forName("UTF-8").decode(byteBuffer);
            }
            // 将channel再次注册到选择器上，监听可读状态
            channel.register(selector,SelectionKey.OP_READ);
            if (msg.length()>0){
                // 输出消息
                DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime time = LocalDateTime.now();
                // LocalDateTime转为String
                String localTime = df.format(time);
                System.out.println(localTime+" : "+"from 客户端："+msg);
                // 向其他客户端转发消息，专门写一个方法处理该逻辑
                sendInfoToOthers(msg,channel);
            }

        }catch (IOException e){
            try {
                System.out.println(channel.getRemoteAddress()+"离线了。。");
                // 取消注册
                key.cancel();
                // 关闭通道
                channel.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }
    // 转发消息给其他客户端（通道）
    private void sendInfoToOthers(String msg, SocketChannel socketChannel) throws IOException {
        System.out.println("服务器转发消息中。。。");
        // 遍历所有注册到selector上的socketChannel，并排除自身
        for (SelectionKey key:selector.keys()){
            //获取每个channel
            Channel targetChannel = key.channel();
            //将自己排除，不需要给自己发送
            if (targetChannel instanceof SocketChannel && targetChannel != socketChannel){
                ((SocketChannel)targetChannel).write(Charset.forName("UTF-8").encode(msg));
            }
        }
    }


    public static void main(String[] args) {
        // 创建一个服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }


}
